package squares;

import java.util.HashMap;

import be.kuleuven.cs.som.annotate.Raw;
import borders.*;

/**
 * this class is a more specific class of squares, it containes only openborders and doors and can never have
 * a wall. this class can teleport.
 * 
 * @version 1.0
 * @author Gilles Groven & Jochen Tombal
 */
public class TransparentSquare extends Square {
	/**
	 * makes a new  transparent Square with the given temperature, humidity, slipperyfloor and the given direction of its door (and makes it closed), and a boolean stating that this square has two doors.
	 * @param temperature
	 * 			the new temperature for this transparentSquare, in the given format
	 * @param type
	 * 		the format for the given temperature
	 * @param humidity
	 * 		the humidity of this transparentSquare
	 * @param slipperyFloor
	 * 		states if the floor of this square is slippery or not
	 * @param direction
	 * 		the direction you want the door to be in, because a transparentSquare has always atleast one door.
	 * @param teleport
	 * 		states if it will be possible to teleport from this square ( but the constructor doesn't state a teleportable square yet)
	 * @param twoDoors
	 * 		states if the transparent square has a door in the opposite direction of the direction of the first door.
	 * @throws InvalidTemperatureException
	 * 		if the given temperature is not a valid temperature for this square
	 * 		| !(isValidTemperature(temperature,type)
	 * @throws InvalidHumidityException
	 * 		if the given humidity is not a valid humidity
	 * 		| !(isValidHumidity(humidity)
	 * @throws IllegalTemperatureTypeException
	 * 		if the given temperatureType is null
	 * 		| type == null
	 * @post
	 * 		the transparentSquare has openBorders in all directions except in the given door direction (and the opposite direction if so specified)
	 * 		| for(Direction direction1: Direction.values())
	 * 		| 	 if(direction1 != direction)
	 * 		|		if(twoDoors && direction1 != Square.getoppositeDirection(direction))
	 * 		|		 	then  new.getBorderAt(direction1) instanceof Open
	 * 		|		else if( twoDoors && direction1 == Square.getoppositeDirection(direction)
	 * 		|		 	then  new.getBorderAt(direction1) instanceof Door
	 * 		|		else
	 * 		|			 getBorderAt(direction1) instanceof Open
	 * 		|	else
	 * 		|		 getBorderAt(direction1) instanceof Door
	 * @post
	 * 		the temperature and humidity of this transparentSquare are valid
	 * 		| isValidTemperature(new.temperature,type) && isValidHumidity(new.humidity)
	 */
	public TransparentSquare(double temperature,TemperatureType type,double humidity,boolean slipperyFloor,boolean teleport,Direction direction,boolean twoDoors) throws InvalidTemperatureException, InvalidHumidityException, IllegalTemperatureTypeException,IllegalArgumentException
	{
		super(temperature,type,humidity,slipperyFloor,teleport);
		if(direction == Direction.FLOOR)
		{
			throw new IllegalArgumentException("no door possible in the floor");
		}
		if(direction == Direction.CEILING)
		{
			if(twoDoors)
			{
				throw new IllegalArgumentException("no door possible in the floor");
			}
		}
		this.setBorder(direction, new Door(this,false));
		if(twoDoors)
		{
			this.setBorder(Square.getOppositeDirection(direction), new Door(this,false));
		}
	}
	/**
	 * this method is a override of the makeBorders of square, it makes all borders of this square a openBorder (and not satisfying the invariant). this
	 * method is only to be used in the constructor of this TransparentSquare
	 */
	@Override
	@Raw
	protected void makeBorders()
	{
		if(this.borders == null)
			this.borders = new HashMap<Direction,Border>();
		if( borders.isEmpty())
			borders.put(Direction.NORTH, new Open(this));borders.put(Direction.EAST,new Open(this));borders.put(Direction.WEST,new Open(this));
			borders.put(Direction.SOUTH,new Open(this));borders.put(Direction.CEILING, new Open(this));borders.put(Direction.FLOOR,new Open(this));
	}
	@Override
	protected boolean isValidBorder(Direction direction,Border border)
	{
		if( border == null || direction == null)
			return false;
		if(!border.hasOnlyOneSquareAttached())
			return false;
		if(border.getAttachedSquare1() != null)
		{
			if(!border.getAttachedSquare1().equals(this))
			{
				if(border.getAttachedSquare2() != null)
				{
					if(!border.getAttachedSquare2().equals(this))
					{
						return false;
					}
				}
			}
		}
		if(border instanceof Door)
		{
			
			for(Direction direction1:Direction.values())
			{
				if(this.getBorderAt(direction1) instanceof Door && Square.getOppositeDirection(direction1) != direction)
					return false;
			}
			return true;
		}
		if(border instanceof Wall)
		{
			return false;
		}
		if(border instanceof Open)
		{
			int numberOfOpen = 0;
			for(Direction direction2:Direction.values())
			{
				if(this.getBorderAt(direction2) instanceof Open)
					numberOfOpen ++;
			}
			if(numberOfOpen == 5)
			{
				return false;
			}
			else
				return true;
		}
		return false;
	}
	@Override
	public boolean BorderCanBeChanged(Direction direction,Border border)
	{
		if( border == null || direction == null)
			return false;
		if(border instanceof Wall)
			return false;
		if(border instanceof Door)
		{
			if(!(this.getBorderAt(direction) instanceof Door) && !(this.getBorderAt(Square.getOppositeDirection(direction)) instanceof Door))
					return false;
		}
		if(border instanceof Open)
		{
			if(!(this.getBorderAt(direction) instanceof Open) && !(this.getBorderAt(Square.getOppositeDirection(direction)) instanceof Door))
					{
						return false;
					}
		}
		if(border.getAttachedSquare1() != null)
		{
			if(!border.getAttachedSquare1().equals(this))
			{
				if(border.getAttachedSquare2() != null)
				{
					if(!border.getAttachedSquare2().equals(this))
					{
						return false;
					}
				}
			}
		}
		if(!border.hasOnlyOneSquareAttached())
		{
			if(borderInOtherDirection(direction,border.getOtherAttachedSquare(this)))
				return false;		
			if((border.getAttachedSquare1().equals(borders.get(direction).getAttachedSquare1()) 
					|| border.getAttachedSquare1().equals(borders.get(direction).getAttachedSquare2()))
					&& (border.getAttachedSquare2().equals(borders.get(direction).getAttachedSquare1()) 
					|| border.getAttachedSquare2().equals(borders.get(direction).getAttachedSquare2())))
			{
				if(isRightDoubleBorderDirection(direction,borders.get(direction).getOtherAttachedSquare(this).getDirectionOfBorder(borders.get(direction))))
					{
						Direction otherDirection = Square.getOppositeDirection(direction);
							if(border instanceof Door)
							{
								if(border.getOtherAttachedSquare(this) instanceof TransparentSquare)
								{
									if(!(border.getOtherAttachedSquare(this).getBorderAt(otherDirection) instanceof Door) && !(border.getOtherAttachedSquare(this).getBorderAt(Square.getOppositeDirection(otherDirection)) instanceof Door))
										return false;
								}
								else if(border.getOtherAttachedSquare(this) instanceof RockSquare)
								{ 
									return false;
								}
								else
								{
									if(border.getOtherAttachedSquare(this).toManyDoors(otherDirection))
									{
										return false;
									}
								}
							}
							else if(border instanceof Open)
							{
								if(border.getOtherAttachedSquare(this) instanceof RockSquare)
								{
									return false;
								}
								else if(border.getOtherAttachedSquare(this) instanceof TransparentSquare)
								{
									if(!(border.getOtherAttachedSquare(this).getBorderAt(otherDirection) instanceof Open) && !(border.getOtherAttachedSquare(this).getBorderAt(Square.getOppositeDirection(otherDirection)) instanceof Door))
									{
										return false;
									}
								}
								else
								{
									if(border.getOtherAttachedSquare(this).toManyOpenBorders(otherDirection))
									{
										return false;
									}
								}
							}
							if(otherDirection == Direction.FLOOR && border instanceof Door && !(border.getOtherAttachedSquare(this) instanceof TransparentSquare))
							{ 
								return false;
							}
					return true;
						}
				else
					return false;
			}
			else
			{
				return false;
			}
			
		}
		else {
			if(!borders.get(direction).hasOnlyOneSquareAttached())
				return false;

		}
		return true;
		
	}

}
